<template>
  <div class="app-container announce-page">
    <el-form
      :model="queryParams"
      ref="queryRef"
      :inline="true"
      v-show="showSearch"
      label-width="80px"
      class="announce-filter"
    >
      <el-form-item label="公告标题" prop="announceTitle">
        <el-input
          v-model="queryParams.announceTitle"
          placeholder="请输入公告标题"
          clearable
          @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="发布人" prop="announceName">
        <el-input
          v-model="queryParams.announceName"
          placeholder="请输入发布人姓名"
          clearable
          @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="公告时间" prop="announceTime">
        <el-date-picker
          clearable
          v-model="queryParams.announceTime"
          type="date"
          value-format="YYYY-MM-DD"
          placeholder="请选择公告时间"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <el-row :gutter="10" class="mb8 announce-ops">
      <el-col :span="1.5">
        <el-button
          type="primary"
          plain
          icon="Plus"
          @click="handleAdd"
          v-hasPermi="['manage:announce:add']"
        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="Edit"
          :disabled="single"
          @click="handleUpdate"
          v-hasPermi="['manage:announce:edit']"
        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="Delete"
          :disabled="multiple"
          @click="handleDelete"
          v-hasPermi="['manage:announce:remove']"
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="Download"
          @click="handleExport"
          v-hasPermi="['manage:announce:export']"
        >导出</el-button>
      </el-col>
      <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <div class="announce-layout">
      <aside class="announce-sidebar" v-loading="clubLoading">
        <div class="sidebar-title">社团列表</div>
        <el-scrollbar class="sidebar-scroll">
          <div
            :class="['sidebar-item', { active: activeClubId === null }]"
            @click="handleClubClick(null)"
          >
            全部社团
          </div>
          <div
            v-for="club in clubList"
            :key="club.clubId"
            :class="['sidebar-item', { active: activeClubId === club.clubId }]"
            @click="handleClubClick(club.clubId)"
          >
            {{ club.clubName }}
          </div>
        </el-scrollbar>
      </aside>

      <main class="announce-list" v-loading="loading">
        <div class="announce-cards">
          <div
            v-for="item in announceList"
            :key="item.announceId"
            :class="['announce-card', { 'announce-card--active': selectedIds.includes(item.announceId) }]"
          >
            <div
              class="announce-card__selector"
              @click.stop="toggleSelect(item.announceId)"
            >
              <span class="selector-dot" :class="{ 'is-checked': selectedIds.includes(item.announceId) }">
                <el-icon v-if="selectedIds.includes(item.announceId)">
                  <Check />
                </el-icon>
              </span>
            </div>
            <div
              class="announce-card__body"
              @click="openDetail(item)"
            >
              <div class="announce-card__image-wrapper">
                <img
                  v-if="item.image"
                  :src="resolveImage(item.image)"
                  alt="公告图片"
                  class="announce-card__image"
                />
                <div v-else class="announce-card__image announce-card__image--placeholder">
                  暂无图片
                </div>
              </div>
              <div class="announce-card__content">
                <h3 class="announce-card__title">{{ item.announceTitle }}</h3>
                <div class="announce-card__meta">
                  <span class="announce-card__publisher">
                    {{ item.announceName || '未知发布者' }}
                  </span>
                  <span class="announce-card__time">{{ formatAnnounceTime(item.announceTime) }}</span>
                  <span
                    v-if="item.clubId"
                    class="announce-card__club"
                  >
                    {{ getClubName(item.clubId) }}
                  </span>
                </div>
                <p class="announce-card__excerpt">{{ getExcerpt(item.announceContent) }}</p>
                <div class="announce-card__actions">
                  <el-button type="primary" link @click.stop="openDetail(item)">查看详情</el-button>
                  <el-button
                    link
                    type="primary"
                    icon="Edit"
                    @click.stop="handleUpdate(item)"
                    v-hasPermi="['manage:announce:edit']"
                  >修改</el-button>
                  <el-button
                    link
                    type="primary"
                    icon="Delete"
                    @click.stop="handleDelete(item)"
                    v-hasPermi="['manage:announce:remove']"
                  >删除</el-button>
                </div>
              </div>
            </div>
          </div>
        </div>
        <el-empty v-if="!loading && announceList.length === 0" description="暂无公告"></el-empty>
        <pagination
          v-show="total > 0"
          :total="total"
          v-model:page="queryParams.pageNum"
          v-model:limit="queryParams.pageSize"
          @pagination="getList"
        />
      </main>
    </div>

    <el-dialog
      v-model="detailVisible"
      width="720px"
      append-to-body
      :title="detailData?.announceTitle || '公告详情'"
      class="announce-detail-dialog"
    >
      <div v-if="detailData" class="announce-detail">
        <div class="announce-detail__header">
          <h2 class="announce-detail__title">{{ detailData.announceTitle }}</h2>
          <div class="announce-detail__meta">
            <span>{{ detailData.announceName || '未知发布者' }}</span>
            <span>{{ formatAnnounceTime(detailData.announceTime) }}</span>
            <span v-if="detailData.clubId">{{ getClubName(detailData.clubId) }}</span>
          </div>
        </div>
        <div class="announce-detail__cover" v-if="detailData.image">
          <img :src="resolveImage(detailData.image)" alt="公告图片" />
        </div>
        <div class="announce-detail__content" v-html="detailData.announceContent"></div>
      </div>
    </el-dialog>

    <el-dialog :title="title" v-model="open" width="620px" append-to-body>
      <el-form ref="announceRef" :model="form" :rules="rules" label-width="100px" class="announce-form">
        <el-form-item label="公告标题" prop="announceTitle">
          <el-input v-model="form.announceTitle" placeholder="请输入公告标题" />
        </el-form-item>
        <el-form-item label="公告内容" prop="announceContent">
          <editor v-model="form.announceContent" :min-height="192" />
        </el-form-item>
        <el-form-item label="公告图片" prop="image">
          <image-upload v-model="form.image" />
        </el-form-item>
        <el-form-item label="发布人" prop="announceName">
          <el-input v-model="form.announceName" placeholder="请输入发布人姓名" />
        </el-form-item>
        <el-form-item label="公告时间" prop="announceTime">
          <el-date-picker
            clearable
            v-model="form.announceTime"
            type="date"
            value-format="YYYY-MM-DD"
            placeholder="请选择公告时间"
          />
        </el-form-item>
        <el-form-item label="所属社团" prop="clubId">
          <el-select v-model="form.clubId" placeholder="请选择所属社团">
            <el-option
              v-for="club in clubList"
              :key="club.clubId"
              :label="club.clubName"
              :value="club.clubId"
            />
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="cancel">取 消</el-button>
          <el-button type="primary" @click="submitForm">确 定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, toRefs, getCurrentInstance, computed, watch } from "vue"
import { listAnnounce, getAnnounce, delAnnounce, addAnnounce, updateAnnounce } from "@/api/manage/announce"
import { listClub } from "@/api/manage/club"
import { parseTime } from "@/utils/ruoyi"
import { isExternal } from "@/utils/validate"
import { Check } from "@element-plus/icons-vue"

const { proxy } = getCurrentInstance()

const announceList = ref([])
const clubList = ref([])
const clubLoading = ref(false)
const open = ref(false)
const loading = ref(true)
const showSearch = ref(true)
const ids = ref([])
const selectedIds = ref([])
const single = ref(true)
const multiple = ref(true)
const total = ref(0)
const title = ref("")
const activeClubId = ref(null)
const detailVisible = ref(false)
const detailData = ref(null)

const clubMap = computed(() => {
  const map = new Map()
  clubList.value.forEach(item => {
    map.set(item.clubId, item.clubName)
  })
  return map
})

const data = reactive({
  form: {},
  queryParams: {
    pageNum: 1,
    pageSize: 10,
    announceTitle: null,
    announceName: null,
    announceTime: null,
    clubId: null
  },
  rules: {
    announceTitle: [{ required: true, message: "公告标题不能为空", trigger: "blur" }],
    announceContent: [{ required: true, message: "公告内容不能为空", trigger: "blur" }],
    announceName: [{ required: true, message: "发布人不能为空", trigger: "blur" }],
    announceTime: [{ required: true, message: "公告时间不能为空", trigger: "blur" }],
    clubId: [{ required: true, message: "所属社团不能为空", trigger: "change" }]
  }
})

const { queryParams, form, rules } = toRefs(data)

function getList() {
  loading.value = true
  listAnnounce(queryParams.value).then(response => {
    announceList.value = response.rows || []
    total.value = response.total || 0
    selectedIds.value = []
    loading.value = false
  }).catch(() => {
    loading.value = false
  })
}

function getClubList() {
  clubLoading.value = true
  listClub({ pageNum: 1, pageSize: 999 }).then(response => {
    clubList.value = response.rows || []
    clubLoading.value = false
  }).catch(() => {
    clubLoading.value = false
  })
}

function cancel() {
  open.value = false
  reset()
}

function reset() {
  form.value = {
    announceId: null,
    announceTitle: null,
    image: null,
    announceContent: null,
    announceName: null,
    announceTime: null,
    clubId: null,
    createTime: null,
    updateTime: null
  }
  proxy.resetForm("announceRef")
}

function handleQuery() {
  queryParams.value.pageNum = 1
  getList()
}

function resetQuery() {
  proxy.resetForm("queryRef")
  activeClubId.value = null
  queryParams.value.clubId = null
  selectedIds.value = []
  handleQuery()
}

function handleClubClick(clubId) {
  activeClubId.value = clubId
  queryParams.value.clubId = clubId
  queryParams.value.pageNum = 1
  selectedIds.value = []
  getList()
}

function toggleSelect(announceId) {
  if (announceId === undefined || announceId === null) return
  const index = selectedIds.value.indexOf(announceId)
  if (index > -1) {
    selectedIds.value.splice(index, 1)
  } else {
    selectedIds.value.push(announceId)
  }
}

watch(
  selectedIds,
  value => {
    const values = Array.isArray(value) ? [...value] : []
    ids.value = values
    single.value = values.length !== 1
    multiple.value = values.length === 0
  },
  { deep: true, immediate: true }
)

function handleAdd() {
  reset()
  open.value = true
  title.value = "添加公告"
}

function handleUpdate(row = {}) {
  const targetId = row.announceId || ids.value[0]
  if (!targetId) {
    proxy.$modal.msgWarning("请选择需要修改的公告")
    return
  }
  reset()
  getAnnounce(targetId).then(response => {
    form.value = response.data || {}
    open.value = true
    title.value = "修改公告"
  })
}

function submitForm() {
  proxy.$refs["announceRef"].validate(valid => {
    if (valid) {
      if (form.value.announceId != null) {
        updateAnnounce(form.value).then(() => {
          proxy.$modal.msgSuccess("修改成功")
          open.value = false
          getList()
        })
      } else {
        addAnnounce(form.value).then(() => {
          proxy.$modal.msgSuccess("新增成功")
          open.value = false
          getList()
        })
      }
    }
  })
}

function handleDelete(row = {}) {
  const targetIds = row.announceId ? [row.announceId] : ids.value
  if (!targetIds.length) {
    proxy.$modal.msgWarning("请选择需要删除的公告")
    return
  }
  proxy.$modal.confirm(`是否确认删除公告编号为 "${targetIds.join(", ")}" 的数据项？`).then(function () {
    return delAnnounce(row.announceId || targetIds.join(","))
  }).then(() => {
    proxy.$modal.msgSuccess("删除成功")
    selectedIds.value = selectedIds.value.filter(id => !targetIds.includes(id))
    getList()
  }).catch(() => {})
}

function handleExport() {
  proxy.download("manage/announce/export", { ...queryParams.value }, `announce_${new Date().getTime()}.xlsx`)
}

function openDetail(item) {
  detailData.value = item
  detailVisible.value = true
}

function formatAnnounceTime(time) {
  if (!time) return "未知时间"
  return parseTime(time, "{y}-{m}-{d}")
}

function getExcerpt(content) {
  if (!content) return "暂无内容"
  const text = content.replace(/<[^>]+>/g, "").replace(/&nbsp;/gi, " ").trim()
  return text.length > 80 ? `${text.slice(0, 80)}...` : text
}

function resolveImage(src) {
  if (!src) return ""
  const [first] = String(src).split(",")
  if (isExternal(first)) {
    return first
  }
  return `${import.meta.env.VITE_APP_BASE_API}${first}`
}

function getClubName(id) {
  return clubMap.value.get(id) || "未知社团"
}

getClubList()
getList()
</script>

<style scoped>
.announce-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.announce-filter {
  background: #fff;
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(31, 35, 41, 0.05);
}

.announce-ops {
  margin-bottom: 0 !important;
}

.announce-layout {
  display: flex;
  gap: 16px;
}

.announce-sidebar {
  width: 220px;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(31, 35, 41, 0.08);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.sidebar-title {
  padding: 16px;
  font-weight: 600;
  font-size: 16px;
  border-bottom: 1px solid #f0f2f5;
}

.sidebar-scroll {
  max-height: 520px;
}

.sidebar-item {
  padding: 12px 16px;
  cursor: pointer;
  transition: all 0.2s ease;
  color: #555;
}

.sidebar-item:hover {
  background: #f6f9ff;
  color: #1677ff;
}

.sidebar-item.active {
  background: linear-gradient(90deg, rgba(22, 119, 255, 0.12), rgba(22, 119, 255, 0.04));
  color: #1677ff;
  font-weight: 600;
}

.announce-list {
  flex: 1;
  background: #fff;
  border-radius: 12px;
  box-shadow: 0 4px 12px rgba(31, 35, 41, 0.08);
  padding: 20px;
  display: flex;
  flex-direction: column;
  gap: 20px;
  min-height: 600px;
}

.announce-cards {
  display: grid;
  gap: 16px;
}

.announce-card {
  position: relative;
  border: 1px solid transparent;
  border-radius: 12px;
  overflow: hidden;
  background: #f9fbff;
  transition: all 0.2s ease;
}

.announce-card--active,
.announce-card:hover {
  border-color: rgba(22, 119, 255, 0.3);
  box-shadow: 0 8px 20px rgba(22, 119, 255, 0.12);
  background: #fff;
}

.announce-card__selector {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 2;
}

.selector-dot {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  border: 2px solid #dcdfe6;
  background-color: rgba(255, 255, 255, 0.95);
  color: var(--el-color-primary);
  transition: all 0.2s ease;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.selector-dot:hover {
  border-color: var(--el-color-primary);
}

.selector-dot.is-checked {
  background: var(--el-color-primary);
  border-color: var(--el-color-primary);
  color: #fff;
  box-shadow: 0 6px 18px rgba(64, 158, 255, 0.38);
}

.announce-card__body {
  display: flex;
  gap: 20px;
  padding: 20px 24px 24px 24px;
  cursor: pointer;
}

.announce-card__image-wrapper {
  width: 200px;
  height: 130px;
  flex-shrink: 0;
  border-radius: 12px;
  overflow: hidden;
  background: #eef3ff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.announce-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.announce-card__image--placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  color: #9aa4b3;
  font-size: 14px;
  background: linear-gradient(135deg, #f2f4f8 0%, #e9eef7 100%);
}

.announce-card__content {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.announce-card__title {
  font-size: 20px;
  margin: 0;
  color: #1f2329;
  font-weight: 600;
  line-height: 1.4;
}

.announce-card__meta {
  display: flex;
  gap: 12px;
  font-size: 14px;
  color: #6b7785;
  flex-wrap: wrap;
}

.announce-card__publisher,
.announce-card__time,
.announce-card__club {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.announce-card__publisher::before {
  content: "发布人";
  font-size: 12px;
  color: #a0a8b3;
  background: #f0f4ff;
  border-radius: 10px;
  padding: 2px 6px;
}

.announce-card__time::before {
  content: "发布时间";
  font-size: 12px;
  color: #a0a8b3;
  background: #f5f5f5;
  border-radius: 10px;
  padding: 2px 6px;
  margin-right: 6px;
}

.announce-card__club::before {
  content: "所属社团";
  font-size: 12px;
  color: #a0a8b3;
  background: #e8f5ff;
  border-radius: 10px;
  padding: 2px 6px;
  margin-right: 6px;
}

.announce-card__excerpt {
  margin: 0;
  font-size: 15px;
  color: #4a5563;
  line-height: 1.6;
  max-height: 72px;
  overflow: hidden;
}

.announce-card__actions {
  display: flex;
  gap: 12px;
  align-items: center;
  margin-top: auto;
}

.announce-detail-dialog :deep(.el-dialog__body) {
  max-height: 60vh;
  overflow-y: auto;
}

.announce-detail {
  display: flex;
  flex-direction: column;
  gap: 16px;
  color: #1f2329;
}

.announce-detail__title {
  margin: 0;
  font-size: 22px;
  font-weight: 700;
}

.announce-detail__meta {
  display: flex;
  gap: 16px;
  color: #6b7785;
  font-size: 14px;
  flex-wrap: wrap;
}

.announce-detail__cover {
  border-radius: 12px;
  overflow: hidden;
  background: #f4f6fb;
}

.announce-detail__cover img {
  width: 100%;
  display: block;
  object-fit: cover;
}

.announce-detail__content :deep(img) {
  max-width: 100%;
  border-radius: 8px;
}

.announce-detail__content :deep(p) {
  line-height: 1.8;
  color: #444;
}

.announce-detail__content {
  font-size: 16px;
  line-height: 1.8;
  word-break: break-word;
  white-space: pre-wrap;
}

.announce-detail__content :deep(pre) {
  white-space: pre-wrap;
}

.announce-form :deep(.ql-container) {
  border-radius: 8px;
}

.announce-form :deep(.ql-editor) {
  white-space: pre-wrap;
  word-break: break-word;
}

.announce-form {
  padding-right: 16px;
}

@media (max-width: 1200px) {
  .announce-layout {
    flex-direction: column;
  }

  .announce-sidebar {
    width: 100%;
    flex-direction: row;
    padding: 16px;
  }

  .sidebar-scroll {
    width: 100%;
    max-height: none;
  }

  .sidebar-item {
    border-radius: 10px;
    margin-bottom: 8px;
  }

  .announce-card__body {
    flex-direction: column;
    align-items: flex-start;
  }

  .announce-card__image-wrapper {
    width: 100%;
    height: 180px;
  }
}
</style>
